#ifndef _OOPTHREAD_
#define _OOPTHREAD_

/** Object-oriented pthreads wrapper.
 *
 * Written by Alex Jakushev,
 * mailto:alex.jakushev@gmail.com
 */

#include <pthread.h>
#include <cassert>

/// Global namespace of the library
namespace oopt {

/// Namespace for thread-specific objects
namespace thread {

class Manager;

namespace Internal {

/// General ancestor of all thread function objects
struct GeneralFunction
{
public:
  /// This is the main thread function, override it in children
  virtual void* main( void ) = 0;

  virtual ~GeneralFunction( void ) {
    // std::cout << "~GeneralFunction  " << std::endl; // debug
  }
};

template<typename Data> struct CustomFunctionWrapper : 
  public GeneralFunction
{
  typedef void* PlainFunc( const Data& );

protected:
  PlainFunc* const m_Func;

  CustomFunctionWrapper( PlainFunc* f ) :
    m_Func( f )
  { }
};



/// Thread function class, used as a wrapper for 
// plain functions
//
// Copies all the thread initial data
template<typename Data> struct FunctionWrapper : // tags: data copy
  public CustomFunctionWrapper<Data>
{
  friend class Manager;

  typedef typename CustomFunctionWrapper<Data>::PlainFunc PlainFunc;

private:
  const Data m_Data;

protected:
  FunctionWrapper( PlainFunc* f, const Data& d ) :
    CustomFunctionWrapper<Data>( f ),
    m_Data( d )
  { }

public:
  virtual void* main( void ) {
    return m_Func( this->m_Data );
  }
};

/// Thread function class, used as a wrapper for 
// plain functions
//
// Copies all the thread initial data
template<typename Data> struct FunctionWrapperDataPtr : // tags: pointer to data
  public CustomFunctionWrapper<Data>
{
  friend class Manager;

  typedef typename CustomFunctionWrapper<Data>::PlainFunc PlainFunc;

private:
  const Data& m_Data;

protected:
  FunctionWrapperDataPtr( PlainFunc* f, const Data& d ) :
    CustomFunctionWrapper<Data>( f ),
    m_Data( d )
  { }

public:
  virtual void* main( void ) {
    return m_Func( this->m_Data );
  }
};

}

/** General ancestor of all classes encapsulating 
 * thread function, that contain some initial data 
 * with them.
 *
 * Use this class as a base class for your function 
 * object.
 */
template<typename Data> struct CustomDataFunction : 
  public Internal::GeneralFunction
{
  friend class Manager;

protected:
  /** Contains data passed to thread.
   *
   * This data is allocated dynamically, and *not*
   * in the constructor. This is done because Manager
   * takes care of Data setting explicitely after create.
   *
   * To my opinion, making data class const member and
   * allocating it via constructor looks better, however,
   * this will make user write additional constructor
   * code every time it makes a descendant of CustomDataFunction,
   * and this is not what I aim at.
   */
  std::auto_ptr<const Data> m_Data;
};



/** Thread manager class.
 *
 * The thread manager is a kind of wrapper around pthread_t
 * data type. Provides handy access to all thread management
 * routines.
 */
class Manager {
private:
  /// Thread handle
  pthread_t m_Handle;

  /// Actual thread function ...
  static void* ThreadFunc( void* tf )
  {
    std::auto_ptr< Internal::GeneralFunction > func_o( 
        static_cast< Internal::GeneralFunction* >( tf ) );
    return func_o->main();
  }

  void CreateThread( Internal::GeneralFunction* f )
  {
    // TODO : check if thread already launched

    int res = pthread_create(
        &m_Handle,
        NULL, // TODO : may be changed later
        ThreadFunc,
        f );
    if ( res != 0 )
      throw "Thread starting failed !!!";  // TODO : something more elaborate ?
  }

protected:

public:
  /// Default constructor - doesn't do anything right away,
  // but allows to launch thread later.
  Manager( void )
  { }

  /// Constructor that automatically launches simple thread
  template<class Data> Manager( // tags : autostart with plain function
      typename Internal::FunctionWrapper<Data>::PlainFunc* plf, 
      const Data& d )
  {
    Launch( plf, d );
  }

  virtual ~Manager( void ) {
  }

public:
  /// For low-level operations
  const pthread_t& Handle( void ) const
  { return m_Handle; }

public:
  template< class FuncObj, class Data > void Launch( const Data& d )
  {
    std::auto_ptr< FuncObj > func( new FuncObj );
    CustomDataFunction<Data>& f = *func;  // if this line causes compilation error, your function object is not a descendant of CustomDataFunction<Data>
    f.m_Data.reset( new Data( d ) );

    CreateThread( &f );

    // if everything is OK
    func.release();
  }

  template<class Data> void Launch( // tags : plain function, data copied
      typename Internal::FunctionWrapper<Data>::PlainFunc* plf, 
      const Data& d )
  {
    std::auto_ptr< Internal::FunctionWrapper<Data> > 
      func( new Internal::FunctionWrapper<Data>( plf, d ) );

    CreateThread( func.get() );

    // if everything is OK
    func.release();
  }
};

}

}

#endif // _OOPTHREAD_

// vim: set expandtab list filetype=cpp :
